/**
 * @particulars 对模板进行编译
 */

import { parseHTML } from "./parse";

// 匹配的是插值表达式：{{name}}
const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g;

// 转字符串
function valueToString(value) {
  let result;
  try {
    result = JSON.stringify(value);
  } catch (err) {
    console.error(err);
  }

  return result;
}

// 4、对节点的属性进行处理
function genProps(attrs) {
  let str = '';
  for (let i = 0; i < attrs.length; i++) {
    // 节点的属性
    const attr = attrs[i];

    // 对于节点的style 属性特殊处理，{name: 'style', value: 'border: 1px solid; color: #ccc;'}
    if (attr.name === 'style') {
      const values = attr.value[attr.value.length - 1] === ';' ? attr.value.split(';').slice(0, -1) : attr.value.split(';');

      attr.value = values.reduce((pre, item) => {
        const [key, value] = item.trim().split(':');
        pre[key] = value.trim();

        return pre;
      }, {});
    }

    str += `${attr.name}: ${valueToString(attr.value)},`;
  }

  return `{${str.slice(0, -1)}}`;
}

// 3、对子节点内容进行处理
function gen(node) {
  // 当前子元素为节点
  if (node.type === 1) {
    return codegen(node);
  } else {
    // 当前子元素为文本内容
    const text = node.text;

    // 是否为纯文本
    if (!defaultTagRE.test(text)) {
      return `_v(${valueToString(text)})`;
    } else {
      const tokens = []; // 保存匹配到的结果
      let match; // 匹配的结果
      defaultTagRE.lastIndex = 0; // 保证defaultTagRE.exec() 持续匹配
      let lastIndex = 0; // 标识当前匹配到的字符位置

      // 如果有{{}}，就需要一直匹配
      while (match = defaultTagRE.exec(text)) {
        // 记录当前匹配到的位置
        const index = match.index;

        // 处理纯文本在{{}} 之前的情况（hello {{name}}）
        if (index > lastIndex) tokens.push(valueToString(text.slice(lastIndex, index)));

        lastIndex = index + match[0].length;

        // 处理{{}} 里的内容（{{name}}）
        tokens.push(`_s(${match[1].trim()})`);
      }

      // 将{{}} 都匹配完了，处理纯文本在{{}} 之后的情况（hello {{name}} world）
      if (lastIndex < text.length) tokens.push(valueToString(text.slice(lastIndex)));

      return `_v(${tokens.join('+')})`;
    }
  }
}

// 3、处理children 里面的所有子元素（文本和子节点）
function genChildren(children) {
  return children.map(child => gen(child)).join(',');
}

// 2、对ast 语法树进行处理，_c('div', {style: {"border":" 1px solid"}}, _v(_s(name)+"---6666---"))
// _c(标签元素，节点属性，子节点包含文本内容) => 处理节点，
// _v(文本内容包含插值语法) => 处理文本内容，
// _s(状态数据) => 处理插值语法{{}}
function codegen(ast) {
  const children = genChildren(ast.children);
  const code = `_c('${ast.tag}',${ast.attrs.length > 0 ? genProps(ast.attrs) : 'null'}${ast.children.length > 0 ? `,${children}` : ''})`;

  return code;
}

// 1、对模板进行编译处理
export function compilerToFunction(template) {
  // 1-1、将template 转化成ast 语法树
  const ast = parseHTML(template);

  // 1-2、生成render 方法（render 方法执行返回的结果 就是虚拟dom）
  // 模板引擎的实现：with + new Function()
  let code = codegen(ast);

  // with 创建作用域，可以指定变量的取值 this.name => vm.name
  code = `with(this){return ${code}}`;
  // new Function 可以将处理好的ast 语法树（字符串）转化为函数
  const render = new Function(code);

  return render;
}